#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

#include <imp/imp_audio.h>
#include <imp/imp_log.h>

#include "testopt.h"

#define TAG "test_adec"

#define AEC_SAMPLE_RATE 8000
#define AEC_SAMPLE_TIME 10

#define IMP_AUDIO_BUF_SIZE (5 * (AEC_SAMPLE_RATE * sizeof(short) * AEC_SAMPLE_TIME / 1000))
#define IMP_AUDIO_RECORD_NUM 200

#define TMP_RECORD_FILE "tmp_record.pcm" /*tmp record file for encode*/

extern TEST_ARGS testArgs;
extern char fileNameBuf[NAME_BUF_SIZE];

static char inFileName[256] = "";
static char outFileName[256] = "ao";

static int type = -1;

static void *audio_play_thread(void *argv)
{
	unsigned char *buf = NULL;
	int size = 0;
	int ret = -1;

	if(argv == NULL) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: Please input the play file name.\n", __func__);
		return NULL;
	}

	buf = (unsigned char *)malloc(IMP_AUDIO_BUF_SIZE);
	if(buf == NULL) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: malloc audio buf error\n", __func__);
		return NULL;
	}

	FILE *play_file = fopen(TMP_RECORD_FILE, "rb");
	if(play_file == NULL) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: fopen %s failed\n", __func__, TMP_RECORD_FILE);
		return NULL;
	}

	/* Step 1: set public attribute of AO device. */
	int devID = 0;
	IMPAudioIOAttr attr;
	attr.samplerate = AUDIO_SAMPLE_RATE_8000;
	attr.bitwidth = AUDIO_BIT_WIDTH_16;
	attr.soundmode = AUDIO_SOUND_MODE_MONO;
	attr.frmNum = 20;
	attr.numPerFrm = 400;
	attr.chnCnt = 1;
	ret = IMP_AO_SetPubAttr(devID, &attr);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "set ao %d attr err: %d\n", devID, ret);
		return NULL;
	}

	memset(&attr, 0x0, sizeof(attr));
	ret = IMP_AO_GetPubAttr(devID, &attr);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "get ao %d attr err: %d\n", devID, ret);
		return NULL;
	}

	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr samplerate:%d\n", attr.samplerate);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr   bitwidth:%d\n", attr.bitwidth);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr  soundmode:%d\n", attr.soundmode);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr     frmNum:%d\n", attr.frmNum);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr  numPerFrm:%d\n", attr.numPerFrm);
	IMP_LOG_INFO(TAG, "Audio Out GetPubAttr     chnCnt:%d\n", attr.chnCnt);

	/* Step 2: enable AO device. */
	ret = IMP_AO_Enable(devID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "enable ao %d err\n", devID);
		return NULL;
	}

	/* Step 3: enable AI channel. */
	int chnID = 0;
	ret = IMP_AO_EnableChn(devID, chnID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio play enable channel failed\n");
		return NULL;
	}

	/* Step 4: Set audio channel volume. */
	int chnVol = 60;
	ret = IMP_AO_SetVol(devID, chnID, chnVol);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Play set volume failed\n");
		return NULL;
	}

	ret = IMP_AO_GetVol(devID, chnID, &chnVol);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Play get volume failed\n");
		return NULL;
	}
	IMP_LOG_INFO(TAG, "Audio Out GetVol    vol:%d\n", chnVol);

	int aogain = 28;
	ret = IMP_AO_SetGain(devID, chnID, aogain);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Set Gain failed\n");
		return NULL;
	}

	ret = IMP_AO_GetGain(devID, chnID, &aogain);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio Record Get Gain failed\n");
		return NULL;
	}
	IMP_LOG_INFO(TAG, "Audio Out GetGain    gain : %d\n", aogain);

	int i = 0;
	while(1) {
		size = fread(buf, 1, IMP_AUDIO_BUF_SIZE, play_file);
		if(size < IMP_AUDIO_BUF_SIZE)
			break;

		/* Step 5: send frame data. */
		IMPAudioFrame frm;
		frm.virAddr = (uint32_t *)buf;
		frm.len = size;
		ret = IMP_AO_SendFrame(devID, chnID, &frm, BLOCK);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "send Frame Data error\n");
			return NULL;
		}

		IMPAudioOChnState play_status;
		ret = IMP_AO_QueryChnStat(devID, chnID, &play_status);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "IMP_AO_QueryChnStat error\n");
			return NULL;
		}

		IMP_LOG_INFO(TAG, "Play: TotalNum %d, FreeNum %d, BusyNum %d\n",
				play_status.chnTotalNum, play_status.chnFreeNum, play_status.chnBusyNum);

		if(++i == 100) {
			ret = IMP_AO_PauseChn(devID, chnID);
			if(ret != 0) {
				IMP_LOG_ERR(TAG, "IMP_AO_PauseChn error\n");
				return NULL;
			}

			printf("[INFO] Test : Audio Play Pause test.\n");
			printf("[INFO]      : Please input any key to continue.\n");
			getchar();

			ret = IMP_AO_ClearChnBuf(devID, chnID);
			if(ret != 0) {
				IMP_LOG_ERR(TAG, "IMP_AO_ClearChnBuf error\n");
				return NULL;
			}

			ret = IMP_AO_ResumeChn(devID, chnID);
			if(ret != 0) {
				IMP_LOG_ERR(TAG, "IMP_AO_ResumeChn error\n");
				return NULL;
			}
		}
	}

	/* Step 6: disable the audio channel. */
	ret = IMP_AO_DisableChn(devID, chnID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio channel disable error\n");
		return NULL;
	}

	/* Step 7: disable the audio devices. */
	ret = IMP_AO_Disable(devID);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "Audio device disable error\n");
		return NULL;
	}

	fclose(play_file);
	free(buf);
	pthread_exit(0);
}

static void* audio_decode_thread(void *argv)
{
	char *buf_decode = NULL;
	int ret = -1;

	buf_decode = (char *)malloc(IMP_AUDIO_BUF_SIZE);
	if(buf_decode == NULL) {
		IMP_LOG_ERR(TAG, "[ERROR] %s: malloc audio g711 buf error\n", __func__);
		return NULL;
	}

	FILE *file_decode = fopen(inFileName,"rb");
	if(NULL == file_decode) {
		IMP_LOG_ERR(TAG,"fopen %s failed.\n",inFileName);
		return NULL;
	}

	FILE *file_pcm = fopen(outFileName,"wb");
	if(NULL == file_pcm) {
		IMP_LOG_ERR(TAG,"fopen %s failed.\n",outFileName);
		return NULL;
	}

	/* audio decoder create channel. */
	int adChn = 0;

	IMPAudioDecChnAttr attr;

	int readSize = 0;

	if(0 == type){
		attr.type = PT_ADPCM;
		readSize =  IMP_AUDIO_BUF_SIZE/4;
	}
	else if(1 == type){
		attr.type = PT_G711A;
		readSize = IMP_AUDIO_BUF_SIZE/2;
	}
	else if(2 == type){
		attr.type = PT_G711U;
		readSize = IMP_AUDIO_BUF_SIZE/2;
	}
	else if(3 == type){
		attr.type = PT_G726;
		readSize = IMP_AUDIO_BUF_SIZE/8;
	}
	else {
		printf("fun:%s,the decode type is not invalid\n",__func__);
		return NULL;
	}
	attr.bufSize = 20;
	attr.mode = ADEC_MODE_PACK;
	ret = IMP_ADEC_CreateChn(adChn, &attr);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "imp audio decoder create channel failed\n");
		return NULL;
	}

	ret = IMP_ADEC_ClearChnBuf(adChn);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "IMP_ADEC_ClearChnBuf failed\n");
		return NULL;
	}

	while(1) {

		ret = fread(buf_decode, 1,readSize, file_decode);
		if(ret <= 0)
			break;

		/* Send a frame to decoder. */
		IMPAudioStream stream_in;
		stream_in.stream = (uint8_t *)buf_decode;
		stream_in.len = ret;
		ret = IMP_ADEC_SendStream(adChn, &stream_in, BLOCK);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio encode send frame failed\n");
			return NULL;
		}

		/* get audio decoder frame. */
		IMPAudioStream stream_out;
		ret = IMP_ADEC_PollingStream(adChn, 1000);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio encode polling stream failed\n");
		}

		ret = IMP_ADEC_GetStream(adChn, &stream_out, BLOCK);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio decoder get stream failed\n");
			return NULL;
		}

		/* save the decoder data to the file. */
		fwrite(stream_out.stream, 1, stream_out.len, file_pcm);

		/* release stream. */
		ret = IMP_ADEC_ReleaseStream(adChn, &stream_out);
		if(ret != 0) {
			IMP_LOG_ERR(TAG, "imp audio decoder release stream failed\n");
			return NULL;
		}
	}

	ret = IMP_ADEC_DestroyChn(adChn);
	if(ret != 0) {
		IMP_LOG_ERR(TAG, "imp audio decoder destroy channel failed\n");
		return NULL;
	}

	fclose(file_pcm);
	fclose(file_decode);

	free(buf_decode);
	return 0;
}

static int set_adecTest_param(TEST_ARGS *pTestArgs)
{
	if(NULL == pTestArgs) {
		printf("fun:%s,pTestArgs is empty.\n",__func__);
		return -1;
	}

	if(!strcmp(pTestArgs->inFileName,"")){
		printf("adec test must set input file name.\n");
		return -1;
	}
	strncpy(inFileName,pTestArgs->inFileName,MAX_FILE_NAME_LEN);

	if(strcmp(pTestArgs->outFileName,"") != 0)
		strncpy(outFileName,pTestArgs->outFileName,MAX_FILE_NAME_LEN);

	if(-1 == pTestArgs->type) {
		printf("adec test must set denc type.\n");
		return -1;
	}
	type = pTestArgs->type;

	return 0;

}

int main(int argc,char *argv[])
{
	int ret = -1;

	ret = parse_param(argc,argv);
	if(1 == ret) {
		return 0;
	}

	if(ret < 0) {
		printf("parse_param failed\n");
		return -1;
	} else {
		printf("parse_param Ok.\n");
	}

	ret = set_adecTest_param(&testArgs);
	if(ret != 0)
		return -1;

	printf("inFileName = %s\n",inFileName);
	printf("outFileName = %s\n",outFileName);
	printf("type = %d\n",type);

	printf("Start to test adec.\n");
	printf("Enter any key to continue.\n");
	getchar();

	pthread_t tid_denc,tid_play;

	if(strcmp(outFileName,"ao") != 0){
		ret = pthread_create(&tid_denc,NULL,audio_decode_thread,outFileName);
		if(ret != 0) {
			printf("Create IMP_Audio_Decode_Thread failed\n");
			return -1;
		}
		pthread_join(tid_denc,NULL);

	} else{
		ret = pthread_create(&tid_denc,NULL,audio_decode_thread,TMP_RECORD_FILE);
		if(ret != 0) {
			printf("Create IMP_Audio_Decode_Thread failed\n");
			return -1;
		}
		pthread_join(tid_denc,NULL);

		ret = pthread_create(&tid_play,NULL,audio_play_thread,TMP_RECORD_FILE);
		if(ret != 0){
			printf("Create IMP_Audio_Play_Thread failed.\n");
			return -1;
		}
		pthread_join(tid_play,NULL);

	}

	printf("Finished adec test.\n");
	return 0;
}

